package com.cawring.simple.route;

import org.apache.commons.lang3.StringUtils;

import com.google.common.base.CharMatcher;
import com.google.common.base.Splitter;
import com.google.common.base.Strings;
import com.google.common.collect.Iterables;

/**
 * 
 * <pre>
 * 클래스명 : 라우터 유틸
 * 패키지명 : com.cawring.simple.route
 * 파일명   : RouteUtils.java
 * 설명     :
 * 라우터 파서 및 정보 추출 시, 사용하는 유틸모음
 * </pre>
 * @Author    : 백승건
 * @Date      : 2014. 3. 11.
 * @Version   : v.1.0.0 - 2014. 3. 11. 최초작성
 */
public class RouteUtils
{
	/**
	 * 
	 * <pre>
	 * 메소드명     : 라우터 정보를 추출한다.
	 * 메소드작성일 : 2014. 3. 12.
	 * 메소드설명   :
	 * routes 파일의 라인 정보를 받아 VO에 담는다.
	 * </pre>
	 * @param line : routes 파일을 1line
	 * @return routeVO
	 * @throws RuntimeException
	 */
	public static RouteVO getRouteInfo(String line)
	{
		/* 앞뒤 공백 제거 */
		String routeLine = StringUtils.trimToNull(line);
		
		/* 라우터 정보 : 라인에서 공백 및 탭을 구분하여 라우터 정보를 가지고 온다. */
		CharMatcher token    = CharMatcher.WHITESPACE.or(CharMatcher.is('\t'));            // 공백과 탭으로 구분한다.
		Splitter    splitter = Splitter.on(token).trimResults().omitEmptyStrings();        // 앞뒤공백을 제거하고 값이 빌 경우 구분에서 제외시킨다.
		String[]    words    = Iterables.toArray(splitter.split(routeLine), String.class); // 배열로 담는다.
		
		/* 라우터 정보 유효성 체크 */
		if(words.length != RouteConstants.ROUTE_INFO_ARR_LEN) // 정보구성이 3개가 아닌 경우 exception 처리한다.
		{
			throw new RouteParseException("라우터 정보 작성 시 "+RouteConstants.ROUTE_INFO_ARR_LEN+"구분값만을 요구합니다."+line);
		}
		
		/* url 메소드 정보가 아닌경우 */
		if(!isUrlMethod(words[RouteConstants.ROUTE_INFO_METHOD_IDX]))  
		{
			throw new RouteParseException("라우터 정보 작성 시 METHOD 허용값은 GET, POST입니다."+line);
		}
		
		/* url 메소드 정보가 아닌경우 */
		if(!isController(words[RouteConstants.ROUTE_INFO_PKG_PATH_IDX]))  
		{
			throw new RouteParseException("라우터 정보 작성 시 컨트롤러 정보에 오류가 있습니다."+line);
		}
		
		/* 라우터 정보를 담는다. */
		String method      = words[RouteConstants.ROUTE_INFO_METHOD_IDX];
		String url         = words[RouteConstants.ROUTE_INFO_URL_IDX];
		String packagePath = words[RouteConstants.ROUTE_INFO_PKG_PATH_IDX];
		RouteVO vo = new RouteVO(method, url, packagePath);
		
		return vo;
	}
	
	/**
	 * 
	 * <pre>
	 * 메소드명     : 파싱 제외 대상 SKIP
	 * 메소드작성일 : 2014. 3. 12.
	 * 메소드설명   :
	 * 파싱할 문자열에 COMMENT_MARK가 있는 경우 SKIP한다.
	 * </pre>
	 * @param line : routes 파일의 1line
	 * @return true or false
	 */
	public static boolean isLineSkip(String line)
	{
		/* 앞뒤 공백 제거 */
		String routeLine = StringUtils.trimToNull(line);
		
		/* 빈값 혹은 null일 경우 skip 한다. */
		if(Strings.isNullOrEmpty(routeLine))
		{
			return true;
		}
		
		/* 코멘트 마크가 라인 선두에 있으면 skip한다. */
		if(StringUtils.startsWith(line, RouteConstants.COMMENT_MARK))
		{
			return true;
		}
		
		/* 유효한 문자열 */
		return false;
	}
	
	/**
	 * 
	 * <pre>
	 * 메소드명     : 유효한 URL METHOD 정보가 맞는지 확인한다.
	 * 메소드작성일 : 2014. 3. 11.
	 * 메소드설명   :
	 * GET, POST 정보가 맞는지 문자열 확인한다.
	 * </pre>
	 * @param method : url method
	 * @return GET or POST ? true : false 
	 */
	public static boolean isUrlMethod(String method)
	{
		if(StringUtils.equals(RouteConstants.METHOD_GET, method))
		{
			return true;
		}
		else if(StringUtils.equals(RouteConstants.METHOD_POST, method))
		{
			return true;
		}
		
		return false;
	}
	
	/**
	 * 
	 * <pre>
	 * 메소드명     : 컨트롤러의 존재여부를 확인한다.
	 * 메소드작성일 : 2014. 3. 11.
	 * 메소드설명   :
	 * 라우터 정보값을 통해 클래스, 메소드 명을 추출하고
	 * 클래스 로드를 통해 컨트롤러와 메소드 선언을 통해 각각의
	 * 존재유무를 파악한다.
	 * </pre>
	 * @param packagePath : 클래스+메소드 - ex> com.cawring.idea.controllers.Stock.index
	 * @return true or false
	 */
	public static boolean isController(String packagePath)
	{
		/* 컨트롤러와 메소드 구분값 */
		int lastDotIdx = StringUtils.lastIndexOf(packagePath, "."); 

		/* 구분자가 없는 경우 */
		if(lastDotIdx == -1)
		{
			String errMsg = "라우터 정보 작성 시 Controller 정보는 패키지와 메소드의 구분자로 '.'을 포함하여야 합니다.";
			throw new RouteParseException(errMsg);
		}
			
		/* 컨트롤러(class)명과 메소드명 추출 */
		String controller 	= StringUtils.substring(packagePath, 0, lastDotIdx); // 컨트롤러
		String method 		= StringUtils.substring(packagePath, lastDotIdx+1);  // 메소드명
		
		/* 에러메시지 작성 */
		String contErr   = "Controller가 클래스로 존재하지 않습니다. "+packagePath+" 컨트롤러 : "+controller;
		String methodErr = "Controller에 method가 존재하지 않습니다. "+packagePath+" 메소드 : "+method;
		
		/* 존재여부 확인 */
		try
		{
			Class<?> contClass = Class.forName(controller);  // 컨트롤러 클래스 호출
			Object contObject  = contClass.newInstance();    // 컨트롤러 생성
			contObject.getClass().getDeclaredMethod(method); // 메소드 체크
		}
		catch (ClassNotFoundException e)
		{
			throw new RouteParseException(contErr);
		}
		catch (InstantiationException e)
		{
			throw new RouteParseException(contErr);
		}
		catch (IllegalAccessException e)
		{
			throw new RouteParseException(contErr);
		}
		catch (NoSuchMethodException e)
		{
			throw new RouteParseException(methodErr);
		}
		catch (SecurityException e)
		{
			throw new RouteParseException(methodErr);
		}
		
		return true;
	}
}
